import i18n from "@src/locales";
import axios from "axios";
import moment from 'moment';
import lodash from 'lodash';
import Vue from 'vue';
import Field from '@model/entity/Field';
import shbHttp from '@src/util/http';
import message from '@src/util/message';
axios.prototype['custom'] = shbHttp;

export default class ExecuteCodeClass {
  static #instance: null | ExecuteCodeClass;
  private executeCodeResultMap: Record<string, any> = {};

  public axios = axios;
  public moment = moment;
  public lodash = lodash;
  public message = message;

  public fields = [];
  public formInstance = null;

  constructor(parameter:ExecuteCodeClass) {
    const { fields = [],  formInstance} = parameter;
    this.fields = fields;
    this.formInstance = formInstance;
  }

  static getInstance(parameter: ExecuteCodeClass) {
    if (!ExecuteCodeClass.#instance) {
      ExecuteCodeClass.#instance = new ExecuteCodeClass(parameter);
    }
    return ExecuteCodeClass.#instance;
  }

  /**
   * des 表单字段值的解析方法
   * @param fieldName
   * @param form
   * @private
   */
  private qf_field(fieldName: string, form: any) {
    const regex = /\$(\S*)\$/;
    return fieldName.match(regex)![1];
  }

  /**
   * des 初始化一些沙箱环境中可使用工具或者变量
   * @param form
   * @param config
   * @private
   */
  private initJsSandboxEnv(form: any, config: any) {
    // 默认环境中的一些函数
    const attachedEnv = {
      axios,
      moment,
      lodash,
      message,
      qf_field: this.qf_field,
      formInstance: this.formInstance,
      fields: this.fields,
      Vue
    };
    return { form, config, ...attachedEnv };
  }

  /**
   * des 创建相关js代码块执行环境沙箱
   * @param sync 是否是同步
   * @param code 代码字符串
   * @private
   */
  private initJsSandbox(sync: boolean, code: string) {
    const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
    return sync ? new AsyncFunction('jsSandbox', `{return async function(){ with(jsSandbox){${code}} }}`) : new Function('jsSandbox', `with(jsSandbox) {${code}}`);
  }

  /**
   *
   * @param code 代码code字符串
   * @param form 表单值
   * @param field 当前执行的代码块field
   * @param forceUpdate 是否强制从新执行
   */
  async executeCode(code: string, form: any, field: Field, forceUpdate: boolean = false, config: any = {}) {
    try {
      const existFieldNameValue = this.executeCodeResultMap?.[field.fieldName];
      if(Object.keys(form).length === 0) return JSON.stringify({});
      // 判断form是否有空的情况 不进行重新执行代码
      if(!existFieldNameValue || forceUpdate) {
        // 判断沙箱环境中代码是否同步还是异步
        const sync = field?.setting?.sync || false;
        const executeFun = this.initJsSandbox(sync, code);
        const jsSandboxEnv  = this.initJsSandboxEnv(form, config);
        this.executeCodeResultMap[field.fieldName] = sync ? await (await executeFun.call(jsSandboxEnv, jsSandboxEnv)).call(jsSandboxEnv) : executeFun.call(jsSandboxEnv, jsSandboxEnv);
      }
      if(this.executeCodeResultMap?.[field.fieldName]) return JSON.stringify(this.executeCodeResultMap[field.fieldName]);
      return i18n.t('formSetting.jsCodeBlock.runErrorMsg') as string + i18n.t('formSetting.jsCodeBlock.notObject') as string;

    } catch (error) {
      return error;
    }
  }

  /**
   * @param code 代码code字符串
   * @param multipleSelection 选中的数据
   * @param config 自定义的配置
   */
   async executeCodeForListAndDetail(code: string, multipleSelection: any, config: any = {}) {
    try {
      if(Object.keys(multipleSelection).length === 0) return JSON.stringify({});
      const executeFun = this.initJsSandbox(true, code);
      const jsSandboxEnv  = this.initJsSandboxEnv({}, config);
      const jsCode = await (await executeFun.call(jsSandboxEnv, jsSandboxEnv)).call(jsSandboxEnv);
      return JSON.stringify(jsCode);
    } catch (error) {
      return error;
    }
  }
}
